package com.dopplr.examples
{
    import com.dopplr.api.API;
    import com.dopplr.api.Traveller;
    
    import flash.display.LoaderInfo;
    import flash.display.Sprite;
    import com.dopplr.api.Trip;
    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.events.StatusEvent;
    import flash.events.ErrorEvent;
    import flash.geom.Point;
    
    public class DopplrPrevailingWind extends Sprite
    {
        public var api:API;
        
        public var numBuckets:int = 36;
        public var bucketCounts:Array = [];

        public function DopplrPrevailingWind()
        {
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            
            var token:String = LoaderInfo(root.loaderInfo).parameters.token;
            if (token) {
                api = new API(token);

                api.addEventListener(StatusEvent.STATUS, function(status:StatusEvent):void { trace(status.code); });
                api.addEventListener(ErrorEvent.ERROR, function(error:ErrorEvent):void { trace(error.text); });

                api.loadAuthenticatedUser(function(user:Traveller):void { api.loadTravellerTrips(user, onTripsLoaded); });
            }

            for (var i:int = 0; i < numBuckets; i++) {
                bucketCounts[i] = 0;
            }
        }
        
        public function onTripsLoaded(user:Traveller):void 
        {
            var maxCount:int = 0;
            for each (var trip:Trip in user.trips) {
                if (trip.city == user.homeCity) continue;
                var bearing:Number = getBearing(user.homeCity.latitude, user.homeCity.longitude,
                                                 trip.city.latitude, trip.city.longitude);
                var bearingBucket:int = int(Math.floor(numBuckets * bearing / 360.0));
                trace('trip to ' + trip.city.name + ' has bearing ' + bearing.toFixed(3) + ' from home');
                bucketCounts[bearingBucket] = (bucketCounts[bearingBucket] as int) + 1;
                maxCount = Math.max(bucketCounts[bearingBucket], maxCount);
            }
            
            // how many arcs?
            var angleStep:Number = Math.PI * 2.0 / numBuckets;

            // start and end angles
            var a1:Number = 0;
            var a2:Number = angleStep;

            // min and max radii
            var maxR:Number = Math.min(stage.stageWidth/2,stage.stageHeight/2) * 0.95;
            var minR:Number = maxR / 4.0;

            var center:Point = new Point(stage.stageWidth/2, stage.stageHeight/2);
            
            for (var i:int = 0; i < numBuckets; i++) {
                var count:int = bucketCounts[i] as int;

                var inner:Number = minR;
                var outer:Number = 10 + minR + (maxR-minR-10)*count/maxCount;

                var arc:Arc = new Arc(a1, a2, inner, outer, stripedGray(i,numBuckets));
                addChild(arc);

                // circle centre:
                arc.x = center.x;
                arc.y = center.y;

                a1 = a2;
                a2 += angleStep;                
            }
            
            var labels:Array = [ "N", "E", "S", "W" ];
            var offsets:Array = [ new Point(0, 10), new Point(-10, 0), new Point(0, -10), new Point(10, 0) ];
            for each (var angle:Number in [ 0, 90, 180, 270 ]) {
                var labelAngle:Number = radians(angle) - Math.PI/2;
                var label:Label = new Label(labels.shift() as String);
                var offset:Point = offsets.shift() as Point;
                label.x = center.x + minR * Math.cos(labelAngle);
                label.y = center.y + minR * Math.sin(labelAngle);
                label.x += offset.x;
                label.y += offset.y;
                addChild(label);
            }
            
            var cityLabel:Label = new Label(user.homeCity.name);
            cityLabel.x = center.x;
            cityLabel.y = center.y;
            addChild(cityLabel);
            
        }
                
        /** returns an angle between 0 and 360 in degrees: 0 is north, 90 is east, 180 is south, 270 is west */
        public function getBearing(lat1:Number, lon1:Number, lat2:Number, lon2:Number):Number
        {
            var b:Number;
            with(Math) {
    			lat1 *= PI / 180.0;
    			lon1 *= PI / 180.0;
    			lat2 *= PI / 180.0;
    			lon2 *= PI / 180.0;
    			b = atan2(sin(lon1-lon2)*cos(lat2), cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon1-lon2)) / -(PI/180);
    			b = b < 0 ? 360 + b : b;
            }
			return b;
        }
        
        public function degrees(radians:Number):Number
        {
            return 360.0 * radians / (2 * Math.PI);
        }

        public function radians(degrees:Number):Number
        {
            return (2 * Math.PI) * degrees / 360.0;
        }

        public function stripedGray(v:Number, maxv:Number):int
        {
            var gray:int = int(255 * v / maxv);
            var r:int = v % 2 == 0 ? gray : (gray+128)%255;
            var g:int = r;
            var b:int = r;
            return r << 16 | g << 8 | b;
        }        
        
    }
}

import flash.display.Shape;
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormat;

class Arc extends Shape
{   
    // how many segments per arc?
    private static const arcStep:Number = Math.PI * 2 / 360.0;
    
    private var start:Number;
    private var end:Number;
    private var inner:Number;
    private var outer:Number;
    private var color:int;
    
    public function Arc(start:Number, end:Number, inner:Number, outer:Number, color:int) {
        this.start = start;
        this.end = end;
        this.inner = inner;
        this.outer = outer;
        this.color = color;
        redraw();
    }
    
    public function redraw():void
    {
        // iteration angle for arcs
        var angle:Number;
        // segment coordinate
        var ax:Number;
        var ay:Number;

        graphics.beginFill( color, 1 );

        // outer arc
        graphics.moveTo(outer * Math.cos(start-(Math.PI/2)), outer * Math.sin(start-(Math.PI/2)));
        for (angle = start+arcStep; angle < end; angle += arcStep) {
            ax = outer * Math.cos(angle-(Math.PI/2));
            ay = outer * Math.sin(angle-(Math.PI/2));
            graphics.lineTo(ax,ay);
        }
        graphics.lineTo(outer * Math.cos(end-(Math.PI/2)), outer * Math.sin(end-(Math.PI/2)));

        // reverse direction for inner arc
        graphics.lineTo(inner* Math.cos(end-(Math.PI/2)), inner * Math.sin(end-(Math.PI/2)));
        for (angle = end-arcStep; angle > start; angle -= arcStep) { // -= because we're going backwards now
            ax = inner * Math.cos(angle-(Math.PI/2));
            ay = inner * Math.sin(angle-(Math.PI/2));
            graphics.lineTo(ax,ay);
        }
        graphics.lineTo(inner * Math.cos(start-(Math.PI/2)), inner * Math.sin(start-(Math.PI/2)));

        // back to beginning
        graphics.lineTo(outer * Math.cos(start-(Math.PI/2)), outer * Math.sin(start-(Math.PI/2)));        
    }

}

class Label extends Sprite
{
    public function Label(string:String)
    {
        var tf:TextField = new TextField();
        tf.defaultTextFormat = new TextFormat("Helvetica", "12", 0x404040, true);
        tf.text = string;
        tf.width = tf.textWidth + 4;
        tf.height = tf.textHeight + 4;
        tf.x -= tf.width / 2;
        tf.y -= tf.height / 2;
        addChild(tf);
    }
}

